// generated 2005/6/29 18:27:49 CST by xihels@163.com
//

#if defined __GNUC__ && __GNUC__ < 3
#error This program will crash if compiled with g++ 2.x
// see the dynamic_cast bug in the gtkmm FAQ
#endif //
#include "config.h"
#include <gtkmmconfig.h>
#if GTKMM_MAJOR_VERSION==2 && GTKMM_MINOR_VERSION>2
#define GMM_GTKMM_22_24(a,b) b
#else //gtkmm 2.2
#define GMM_GTKMM_22_24(a,b) a
#endif //

#include "MsgBox.h"
#include "MemberList.h"

#include <gdk/gdkkeysyms.h>
#include <gtkmm/accelgroup.h>

#include <time.h>
#include "config.h"
#include "MsgPage.h"
#include "pixmaps.h"
#include "QQSession.h"
#include <gdk/gdkkeysyms.h>

#include "sounds.h"
#include "ClusterOperate.h"
#include "ClusterSendIM.h"
#include "SendIM.h"
#include "ReceiveIM.h"
#include "ChatLogWin.h"
#define NORMAL_MESSAGE 0

extern CodeConverter g2u;
extern CodeConverter u2g;

int MsgPage::FragmentsBuf::split(QQSession& session,
       	const	 string& message,
	uint32_t uid,
       	bool	 isClusterMessage)
{
    // 粗略判断一下这个字符是不是大于0x127
    // gbk编码的第一个字符大于0x127
    // 有可能是有意义的西文字符(但不常用)
    int listlen = 0;
    int len = message.size();

    if (len < 200 * MAX_SEND_IM_SIZE) {

	srand(time(NULL));
	int messageId = rand() % 0xffff;

	int it1 = 0;
	int it2 = MAX_SEND_IM_SIZE;

	if (!isClusterMessage) {
	    for (;(it2 > it1) && (it2 < len); ) {
		if (message[it2] < 0) // > 0x127
		    it2--;   // 表示是gbk汉字的第一个字节。
		else {
		    sendlist[listlen++].reset(
			    new SendIMPacket(session, 
				string(message.begin() + it1, message.begin() + it2 + 1), 
				uid,
				messageId,
				listlen
				));
		    it1 = it2 + 1;
		    it2 = it1 + MAX_SEND_IM_SIZE;
		}
	    } // for

	    if (it1 < len - 1) { // 还有最后一部分
		sendlist[listlen++].reset(
			new SendIMPacket(session, 
			    string(message.begin() + it1, message.begin() + len), 
			    uid,
			    messageId,
			    listlen));

	    }

	    for (int i = 0; i < listlen; i++)
		static_cast<SendIMPacket*>(sendlist[i].get())->
		    setTotalFragments(listlen);
	    
	} else { // 群消息
	    for (;(it2 > it1) && (it2 < len); ) {
		if (message[it2] < 0) // > 0x127
		    it2--;   // 表示是gbk汉字的第一个字节。
		else {
		    sendlist[listlen++].reset(
			    new ClusterSendIMPacket(session, 
				string(message.begin() + it1, message.begin() + it2 + 1), 
				uid,
				messageId,
				listlen
				));
		    it1 = it2 + 1;
		    it2 = it1 + MAX_SEND_IM_SIZE;
		}
	    } // for

	    if (it1 < len - 1) { // 还有最后一部分
		sendlist[listlen++].reset(
			new ClusterSendIMPacket(session, 
			    string(message.begin() + it1, message.begin() + len), 
			    uid,
			    messageId,
			    listlen));

	    }

	    for (int i = 0; i < listlen; i++)
		static_cast<ClusterSendIMPacket*>(sendlist[i].get())->
		    setTotalFragments(listlen);

	}
    }
    return listlen;
}

MsgPage::~MsgPage()
{
	delete chatLog;
}
MsgPage::MsgPage(BuddyInfo& _buddyInfo, QQSession& _session):
    buddyInfo(_buddyInfo),
    session(_session),
    titlelabel(NULL),
    messageSender(0),
    messageSequence(0xffffffff),
    hasColor(false),
    memberListShowed(false)
    
{
    int fid_log=0;
    if(buddyInfo.isCluster())
    {
    	fid_log=buddyInfo.getUid();
    }
    else
    {
    	fid_log=buddyInfo.getUid();
    }
    chatLog=new binnote(session.getUid(),fid_log);
    
    
    inputMsgBox = Gtk::manage(new class MsgBox);
    Gtk::ScrolledWindow *scrolledwindowInputMsgBox = 
	Gtk::manage(new class Gtk::ScrolledWindow());

    msgBox = Gtk::manage(new class MsgBox);
    Gtk::ScrolledWindow *scrolledwindowMsgBox = 
    Gtk::manage(new class Gtk::ScrolledWindow());
    
   // logMsgBox=Gtk::manage(new class MsgBox);
   // Gtk::ScrolledWindow * scrolledwindowLogMsgBox=
   // Gtk::manage(new class Gtk::ScrolledWindow());

    Gtk::VPaned *vpanedMsgArea = Gtk::manage(new class Gtk::VPaned());
  //  Gtk::VPaned *anotherArea   = Gtk::manage(new class Gtk::VPaned());
    class Gtk::HPaned* msgPage = this;

    vpanedMsgArea->pack1(*scrolledwindowMsgBox, Gtk::EXPAND | Gtk::SHRINK);
   // anotherArea->pack1(*scrolledwindowInputMsgBox, Gtk::SHRINK|Gtk::EXPAND);
   // vpanedMsgArea->pack2(*anotherArea,Gtk::SHRINK);
    vpanedMsgArea->pack2(*scrolledwindowInputMsgBox,Gtk::SHRINK);
    //anotherArea->pack2(*scrolledwindowLogMsgBox,Gtk::SHRINK);
    msgPage->pack1(*vpanedMsgArea, Gtk::EXPAND );

    GList *focus_chain = NULL; 
    focus_chain = g_list_append(focus_chain, inputMsgBox->gobj());

    if (!buddyInfo.isCluster()) {
	memberList = NULL;
    } else {
	memberList = Gtk::manage(new class MemberList);
	memberList->unset_flags(Gtk::HAS_FOCUS);
	focus_chain = g_list_append(focus_chain, memberList->gobj());


	scrolledwindowMemberList =
	    Gtk::manage(new class Gtk::ScrolledWindow());  

	scrolledwindowMemberList->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
	scrolledwindowMemberList->add(*memberList);

	scrolledwindowMemberList->set_size_request(120, -1);
	msgPage->pack2(*scrolledwindowMemberList,  Gtk::FILL);

	memberList->signal_button_press_event().connect(sigc::mem_fun(*this, 
		&MsgPage::on_memberList_double_click_event), false);

	memberListShowed = true;
    }
    
    scrolledwindowMsgBox->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
    scrolledwindowMsgBox->add(*msgBox);
    msgBox->unset_flags(Gtk::CAN_FOCUS);
    msgBox->set_accepts_tab(false);
    msgBox->set_editable(false);
    
    scrolledwindowInputMsgBox->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
    scrolledwindowInputMsgBox->add(*inputMsgBox);
    inputMsgBox->set_flags(Gtk::HAS_FOCUS);
    
    //scrolledwindowLogMsgBox->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
    //scrolledwindowLogMsgBox->add(*logMsgBox);
    //logMsgBox->unset_flags(Gtk::CAN_FOCUS);
    //logMsgBox->set_accepts_tab(false);

    msgPage->set_focus_chain(Glib::ListHandle<Widget*>(focus_chain, Glib::OWNERSHIP_NONE));

    
    msgPage->show_all();


    inputMsgBox->signal_focus_in_event().connect(
	    sigc::mem_fun(*this, &MsgPage::on_inputMsgBox_focus_in_event), false);


    if (buddyInfo.isCluster()) {
	ClusterInfo& clusterInfo = static_cast<ClusterInfo&>(buddyInfo);
	memberList->refresh(clusterInfo.getMembers());
    }
    
}

bool MsgPage::on_memberList_double_click_event(GdkEventButton *ev)
{
    if (!buddyInfo.isCluster())
	return false;
    if ( (ev->type==GDK_2BUTTON_PRESS ||
		ev->type==GDK_3BUTTON_PRESS) ) {
	uint32_t id = memberList->getSelectMemberid();	
	
	ClusterInfo::Members& members = 
	    (static_cast<ClusterInfo&>(buddyInfo)).getMembers();
	ClusterInfo::Members::value_type val= members.getElement(id);
	
	if (NULL == val)
	    return false;
	session.copyToFriends(val);

	FriendInfo* buddy = session.getFriendInfo(id);
	if (NULL != buddy) {
	    session.showMessagePage(buddy);
	}
    }
    return false;
}

void MsgPage::setTitleColor(bool hascolor)
{
    static const string start = "<span foreground=\"red\">";
    static const string end   = "</span>";

    if ((NULL != titlelabel) &&  (hascolor != hasColor)) {

	if (hascolor && !(inputMsgBox->is_focus())) {
	    char* marktext = g_markup_printf_escaped("<span color='red'>%s</span>",
		    titlelabel->get_text().c_str());
	    titlelabel->set_label(marktext);
	    hasColor = true;
	    g_free(marktext); // C free
	} else {
	    titlelabel->set_label(titlelabel->get_text());
	    hasColor = false;
	}
    }
}

bool MsgPage::on_inputMsgBox_focus_in_event(GdkEventFocus* ev)
{
    setTitleColor(false);
    return false;
}


void MsgPage::on_hide()
{
    titlelabel = NULL;
    msgBox->clear(); 
}

void MsgPage::refreshMembers()
{
    if (! buddyInfo.isCluster()) {
	g_assert(false);
	return ;
    }

    ClusterInfo& clusterInfo = static_cast<ClusterInfo&>(buddyInfo);
    memberList->refresh(clusterInfo.getMembers());
}

void MsgPage::refreshMembersStatus()
{
    if (! buddyInfo.isCluster()) {
	g_assert(false);
	return ;
    }

    ClusterInfo& clusterInfo = static_cast<ClusterInfo&>(buddyInfo);
    memberList->refreshStatus(clusterInfo.getMembers());
}


void MsgPage::beginRefreshMembersStatus()
{
    if (!buddyInfo.isCluster() || !showedMemberList()) 
	return ;
    // 先设置所有成员为离线，再请求在线成员列表
    ClusterInfo::Members& members = (static_cast<ClusterInfo&>(buddyInfo)).getMembers();
    members.forEach(SetStatus(QQ_FRIEND_STATUS_OFFLINE));




    session.sendPacket(OutPacketPtr(
		new GetClusterOnlineMemberPacket(session, buddyInfo.getUid())));
}

void MsgPage::hideMemberList()
{
    if (! buddyInfo.isCluster())
	return;
    if (memberListShowed) {
	scrolledwindowMemberList->hide();
	memberListShowed = false;
    } else {
	scrolledwindowMemberList->show();
	memberListShowed = true;
	beginRefreshMembersStatus();
    }
}

bool MsgPage::showNormalIM(const ReceiveIMPacket& packet)
{
    bool result = false;
    if (messageSequence != packet.getSequence()) {
	if (QQ_IM_NORMAL_TEXT == packet.getNormalIMHeader()->getMessageType()) {
	    messageSequence = packet.getSequence();
	    uint32_t sendTime = packet.getNormalIMHeader()->getSendTime();
	    if (buddyInfo.getNickname().empty()) {
		char idtext[16];
		snprintf(idtext, 16, "%d", buddyInfo.getUid());
		
		showMessage(packet.getNormalIMBody()->getMessage(),
			idtext,
			buddyInfo.getUid(),
			sendTime);
	    } else {
		showMessage(packet.getNormalIMBody()->getMessage(), 
			buddyInfo.getNickname(),
			buddyInfo.getUid(), 
			sendTime);
	    }
	    setTitleColor(true);
	    result = true;
	}
    }

    return result;

}

bool MsgPage::showClusterIM(const ReceiveIMPacket& packet)
{
    bool result = false;
    uint32_t memberid = packet.getClusterIMBody()->getSender();

    if ((messageSender != memberid) ||
	    (messageSequence != packet.getSequence())) {

	messageSender = memberid;
	messageSequence = packet.getSequence();

	//printf("%d, %d\n", messageSender, messageSequence);

	uint32_t sendTime = packet.getClusterIMBody()->getSendTime();
	//cout<<sendTime<<endl;

	ClusterInfo& clusterInfo = static_cast<ClusterInfo&>(buddyInfo);


	ClusterInfo::Members& members = clusterInfo.getMembers();

	ClusterInfo::Members::value_type val = members.getElement(memberid);
	if (NULL == val) {
	    char idtext[16];
	    snprintf(idtext, 16, "%d", memberid);
	    showMessage(packet.getClusterIMBody()->getMessage(),
		    idtext,
		    memberid,
		    sendTime);
	} else {
	    showMessage(packet.getClusterIMBody()->getMessage(),
		    (*val).getNickname(),
		    memberid,
		    sendTime);
	}

	setTitleColor(true);

	uint32_t versionid = packet.getClusterIMBody()->getVersionId();
	if (clusterInfo.getVersionId() != versionid) { // 版本号变了，说明群信息有变动
	    session.sendPacket(OutPacketPtr(
			new GetClusterInfoPacket(session, clusterInfo.getUid())));
	    clusterInfo.setVersionId(versionid);
	}

	result = true;
	// fprintf(stderr, "versionId = %08x\n", packet.getClusterIMBody()->getVersionId());
    }

    return result;

}
void MsgPage::showLog(int time_msg)
{
	int x,y;
	int w,h;
	(this->get_parent_window())->get_position(x,y);
	(this->get_parent_window())->get_size(w,h);
	ChatLogWin* logwin=new ChatLogWin(chatLog);
	logwin->move(x,y+h);
	logwin->resize(w,200);
//	logwin->showLog(time_msg);
/*
//	printf("%d time_msg!\n",time_msg);
	printf("show log called!\n");
	MsgBox *temp;
	temp=msgBox;
	msgBox=logMsgBox;
	string msg;
	string nickname;
	int qqnum;
	int sendtime; 
	time_t tm_now;
	bool time_only=true;//read out only time info to search
	time(&tm_now);
	msgBox->clear();
	while(chatLog->read_msg(qqnum,nickname,sendtime,msg,time_only)==false)
	{

//	printf("%d",i);
		if((tm_now-sendtime<=time_msg))
		if(time_only)
		{
			time_only=false;
		}
		else 
		{
			msgBox->showTitle(nickname,qqnum,sendtime);
			msgBox->showMessage(msg);
			msgBox->showSimpleText("\n");//cannot reach direct output now.'\n' won't work.
		}
		//printf("tm_diff=%d,time_msg=%d\n",tm_now-sendtime,time_msg);
	};
	msgBox=temp;
	temp=NULL;
*/	

	
}
void MsgPage::saveLog(const string & filename)
{
 	chatLog->export_txt(filename);
}
void MsgPage::showMessage(const string& message,
	const string& sender,
	uint32_t senderid,
	uint32_t sendtime)
{

    if (senderid == session.getUid()) // 是自己的消息
	msgBox->showTitle(sender, sendtime);
    else {
	msgBox->showTitle(sender, senderid, sendtime);
	sounds::play(sounds::RECEIVE_SOUND);
    }

    msgBox->showMessage(message);
    msgBox->showSimpleText("\n");
//    int qqnum=session.getUid();
   // printf("read to write msg\n");
    chatLog->write_msg(senderid,sender,sendtime,message);
}


void MsgPage::sendMessage()
{
    Glib::ustring utext;
    inputMsgBox->getText(utext);
//    inputMsgBox->clear();
    if (!utext.empty()) {
	const string& text = utext.raw();

	int smileylen = 0;
	uint8_t index = 255;
	int pos1 = 0;
	int pos = 0;
	int len = text.size();

	string gmessage;
	string utmp;

	gmessage.reserve(len);


	if (!buddyInfo.isCluster())
	    msgBox->showTitle(session.getNickname(), time(NULL));


	for (; pos < len;) {
	    pos = text.find('\\', pos);
	    if (string::npos == pos)
		break;
	    else {
		smileylen = SmileyMap::getSmilIndex(&text[pos], len - pos, index);
		if (255 != index) {
		    if (pos1 < pos) {
			utmp = text.substr(pos1, pos - pos1);

			if (!buddyInfo.isCluster())
			    msgBox->showSimpleText(utmp);

			gmessage.append(u2g.convert(utmp));
		    }
		    pos += smileylen;
		    pos1 = pos;
		    gmessage.push_back(0x14);
		    gmessage.push_back(index + 'A');

		    if (!buddyInfo.isCluster())
			msgBox->showSmiley(index);
		} else
		    pos++;
	    }
	}

	if (pos1 < len) {
	    utmp = text.substr(pos1, len - pos1);

	    if (!buddyInfo.isCluster())
		msgBox->showSimpleText(utmp);

	    gmessage.append(u2g.convert(utmp));
	}    

	if (!buddyInfo.isCluster())
	    msgBox->showSimpleText("\n");


	///////// 发送部分
	if (gmessage.size() < MAX_SEND_IM_SIZE) {  // 消息不大，直接发送。

	    if (!buddyInfo.isCluster())
	    {	
	    	session.sendPacket(OutPacketPtr(
		    new SendIMPacket(session, gmessage, 
			buddyInfo.getUid())));
	    }
	    else 
	    {	
	//	printf("Begin to send a message\n");
	    	session.sendPacket(OutPacketPtr(
			    new ClusterSendIMPacket(session, gmessage, 
				buddyInfo.getUid())));
	//	printf("After sent a message\n");
	    }	
	} else {
	    int total = fragmenBuf.split(session, gmessage,
		    buddyInfo.getUid(),
		    buddyInfo.isCluster());

	    for (int i = 0 ; i < total; i++)
		session.sendPacket(fragmenBuf.sendlist[i]);

	} // else(gmessage.size() <...

    if(!buddyInfo.isCluster())
       chatLog->write_msg(session.getUid(),session.getNickname(),time(NULL),gmessage);
    }	
    
    inputMsgBox->clear();
}




